import numpy

from classfile.attribute_extra import ExceptionTable, LineNumber, LocalVariableTableInfo, LocalVarTypeTableInfo, \
    Annotation, InnerClass
from classfile.class_reader import AttributeData, ClassReader, ByteReader
from classfile.constant import ConstantUTF8, ConstantPool


class Attribute:

    def __init__(self):
        # 属性名称 常量池索引
        self.nameIndex: numpy.uint16 = numpy.uint16(0)
        self.length: numpy.uint32 = numpy.uint32(0)
        self.attributeName: ConstantUTF8 = None
        self.attributeData: AttributeData = None

    def fill_data(self, nameIndex, length, attributeName, attributeData):
        self.nameIndex: numpy.uint16 = nameIndex
        self.length: numpy.uint32 = length
        self.attributeName: ConstantUTF8 = attributeName
        self.attributeData: AttributeData = attributeData
        return self

    def readAttr(self, cp: ConstantPool):
        pass

    @classmethod
    def new(cls):
        return cls()


class Code(Attribute):

    def __init__(self):
        super().__init__()
        # 操作数栈的最大深度
        self.exceptionLength = None
        self.codeLength = None
        self.maxLocals = None
        self.maxStack = None
        self.attributeCount = None
        self.maxStack: numpy.uint16 = None
        # 局部变量表的最大slotCount
        self.maxLocals: numpy.uint16 = None
        # 指令长度
        self.codeLength: numpy.uint32 = None
        # 指令序列
        self.codes = []
        # 异常个数
        self.exceptionLength: numpy.uint16 = None
        # 异常列表
        self.exceptionTable = []
        # 属性个数
        self.attributeCount: numpy.uint16 = None
        # 属性列表
        self.attributes = []

    def readAttr(self, cp: ConstantPool):
        self.maxStack = self.attributeData.readUInt16()
        self.maxLocals = self.attributeData.readUInt16()
        self.codeLength = self.attributeData.readUInt32()
        if self.codeLength > 0:
            self.codes = self.attributeData.readBytes(n=self.codeLength)
        self.exceptionLength = self.attributeData.readUInt16()
        if self.exceptionLength > 0:
            for i in range(0, self.exceptionLength):
                e = ExceptionTable(startPc=self.attributeData.readUInt16()
                                   , endPc=self.attributeData.readUInt16()
                                   , handlerPc=self.attributeData.readUInt16(),
                                   catchType=self.attributeData.readUInt16())
                self.exceptionTable.append(e)
        self.attributeCount = self.attributeData.readUInt16()
        if self.attributeCount > 0:
            for i in range(0, self.attributeCount):
                attr = AttributeHelper.newAttr(cr=self.attributeData, cp=cp)
                self.attributes.append(attr)

    def getMaxStack(self):
        return  self.maxStack

    def getMaxLocals(self):
        return self.maxLocals

    def bytes(self):
        return self.codes

    def getLineNumberTable(self):
        for attr in self.attributes:
            if attr.attributeName.value == "LineNumberTable":
                return attr
        return None

    def getExceptionTable(self):
        return self.exceptionTable


class ConstantValue(Attribute):

    def __init__(self):
        super().__init__()
        self.constantValueIndex: numpy.uint16 = None
        self.constantValue = None

    def readAttr(self, cp: ConstantPool):
        self.constantValueIndex = self.attributeData.readUInt16()
        self.constantValue = cp.getConstant(self.constantValueIndex)

    def getConstantValueIndex(self):
        return self.constantValueIndex


class LineNumberTable(Attribute):
    def __init__(self):
        super().__init__()
        self.lineTableLength: numpy.uint16 = None
        self.lineNumbers = []

    def readAttr(self, cp: ConstantPool):
        self.lineTableLength = self.attributeData.readUInt16()
        if self.lineTableLength > 0:
            ln = LineNumber(startPc=self.attributeData.readUInt16(), lineNo=self.attributeData.readUInt16())
            self.lineNumbers.append(ln)


class LocalVariableTable(Attribute):

    def __init__(self):
        super().__init__()
        self.localVarTableLength: numpy.uint16 = None
        self.localVarTable = []

    def readAttr(self, cp: ConstantPool):
        self.localVarTableLength = self.attributeData.readUInt16()
        if self.localVarTableLength > 0:
            for i in range(0, self.localVarTableLength):
                startPc = self.attributeData.readUInt16()
                length = self.attributeData.readUInt16()
                localVariableNameIndex = self.attributeData.readUInt16()
                localVariableDescriptorIndex = self.attributeData.readUInt16()
                localVariableIndex = self.attributeData.readUInt16()
                lv = LocalVariableTableInfo(
                    startPc=startPc, length=length,
                    localVariableNameIndex=localVariableNameIndex,
                    localVariableDescriptorIndex=localVariableDescriptorIndex,
                    localVariableIndex=localVariableIndex,
                    localVariableName=cp.getConstant(localVariableNameIndex),
                    localVariableDescriptor=cp.getConstant(localVariableDescriptorIndex)
                )
                self.localVarTable.append(lv)


class StackMapTable(Attribute):

    def readAttr(self, cp: ConstantPool):
        # 太复杂了 我不是实现了 哎
        pass


class Exceptions(Attribute):

    def __init__(self):
        super().__init__()
        self.exceptionTableLength: numpy.uint16 = None
        self.exceptionTableIndex = []
        self.exceptionTable = []

    def readAttr(self, cp: ConstantPool):
        self.exceptionTableLength = self.attributeData.readUInt16()
        if self.exceptionTableLength > 0:
            for i in range(0, self.exceptionTableLength):
                index = self.attributeData.readUInt16()
                self.exceptionTableIndex.append(index)
                self.exceptionTable.append(cp.getConstant(index=index))


class LocalVariableTypeTable(Attribute):

    def __init__(self):
        super().__init__()
        self.localVarTypeTableLength: numpy.uint16 = None
        self.localVarTypeTables = []

    def readAttr(self, cp: ConstantPool):
        self.localVarTypeTableLength = self.attributeData.readUInt16()
        if self.localVarTypeTableLength > 0:
            for i in range(0, self.localVarTypeTableLength):
                self.localVarTypeTables.append(LocalVarTypeTableInfo(br=self.attributeData, cp=cp))


class Signature(Attribute):
    def __init__(self):
        super().__init__()
        self.signatureIndex: numpy.uint16 = None

    def readAttr(self, cp: ConstantPool):
        self.signatureIndex = self.attributeData.readUInt16()


class SourceFile(Attribute):
    def __init__(self):
        super().__init__()
        self.sourceFileIndex: numpy.uint16 = None
        self.sourceFile: ConstantUTF8 = None

    def readAttr(self, cp: ConstantPool):
        self.sourceFileIndex = self.attributeData.readUInt16()
        self.sourceFile = cp.getConstant(index=self.sourceFileIndex)


class RuntimeVisibleAnnotations(Attribute):

    def __init__(self):
        super().__init__()
        self.annotationNumber: numpy.uint16 = None
        self.annotations = []

    def readAttr(self, cp: ConstantPool):
        self.annotationNumber = self.attributeData.readUInt16()
        if self.annotationNumber > 0:
            for i in range(0, self.annotationNumber):
                self.annotations.append(Annotation(br=self.attributeData))


class InnerClasses(Attribute):

    def __init__(self):
        super().__init__()
        self.numberOfClasses: numpy.uint16 = None
        self.classList = []

    def readAttr(self, cp: ConstantPool):
        self.numberOfClasses = self.attributeData.readUInt16()
        if self.numberOfClasses > 0:
            for i in range(0, self.numberOfClasses):
                self.classList.append(InnerClass(br=self.attributeData))


class BootstrapMethods(Attribute):

    def readAttr(self, cp: ConstantPool):
        pass


class AttributeHelper:
    # 指令属性
    CODE = "Code"
    # 常量值属性
    CONSTANT_VALUE = "ConstantValue"
    # 行号表属性
    LINENUMBER_TABLE = "LineNumberTable"
    # 局部变量表
    LOCAL_VAR_TABLE = "LocalVariableTable"
    STACK_MAP_TABLE = "StackMapTable"
    EXCEPTION = "Exceptions"
    LOCAL_VAR_TYPE_TABLE = "LocalVariableTypeTable"
    SIGN = "Signature"
    SOURCE_FILE = "SourceFile"
    RUNTIME_ANNO = "RuntimeVisibleAnnotations"
    INNER_CLASS = "InnerClasses"
    BOOTSTRAP_METHOD = "BootstrapMethods"
    # ... 其他暂不解析
    attr_dict = {
        CODE: Code.new,
        CONSTANT_VALUE: ConstantValue.new,
        LINENUMBER_TABLE: LineNumberTable.new,
        LOCAL_VAR_TABLE: LocalVariableTable.new,
        STACK_MAP_TABLE: StackMapTable.new,
        EXCEPTION: Exceptions.new,
        LOCAL_VAR_TYPE_TABLE: LocalVariableTypeTable.new,
        SIGN: Signature.new,
        SOURCE_FILE: SourceFile.new,
        RUNTIME_ANNO: RuntimeVisibleAnnotations.new,
        INNER_CLASS: InnerClasses.new,
        BOOTSTRAP_METHOD: BootstrapMethods.new,
    }

    @staticmethod
    def newAttr(cr: ByteReader, cp: ConstantPool):
        nameIndex = cr.readUInt16()
        length = cr.readUInt32()
        attributeName: ConstantUTF8 = cp.getConstant(nameIndex)
        attributeData = AttributeData(data=cr.readBytes(n=length), pos=0)
        attrName = attributeName.value
        if attrName in AttributeHelper.attr_dict.keys():
            c = AttributeHelper.attr_dict[attributeName.value]()
            c.fill_data(nameIndex=nameIndex, length=length, attributeName=attributeName,
                        attributeData=attributeData)
            c.readAttr(cp=cp)
            return c
        else:
            print("暂不支持的属性:{}".format(attrName))
            return Attribute().fill_data(nameIndex=nameIndex, length=length, attributeName=attributeName,
                                         attributeData=attributeData)
